#![deny(clippy::all)]

use std::{
  fs::File,
  io::{self, BufRead},
  path::Path,
};

use napi::{bindgen_prelude::AsyncTask, JsNumber, Task};

#[macro_use]
extern crate napi_derive;

// we want our function to be a promise to be executed asynchronously to not block the event loop in nodejs
// and for that we need to create this weird structs AsyncReadCsv and impl the Task trait for it.

#[napi]
pub fn read_csv_async(path: String) -> AsyncTask<AsyncReadCsv> {
  AsyncTask::new(AsyncReadCsv { path })
}
// AsyncTask<AsyncReadCsv>，这是一个异步任务对象，用于在后台执行 AsyncReadCsv 结构体中的逻辑。

pub struct AsyncReadCsv {
  path: String,
}

impl Task for AsyncReadCsv {
  type Output = i64;

  type JsValue = JsNumber;

  fn compute(&mut self) -> napi::Result<Self::Output> {
    Ok(read_csv(self.path.clone()))
  }

  fn resolve(&mut self, env: napi::Env, output: Self::Output) -> napi::Result<Self::JsValue> {
    env.create_int64(output)
  }
}

// this is the main function that receive the path to the csv file
// and start processing the data line by line
fn read_csv(path: String) -> i64 {
  let lines: io::Lines<io::BufReader<File>> = read_lines(Path::new(&path)).unwrap();

  let mut sum = 0;

  for line in lines {
    if let Ok(ip) = line { // 尝试将每一行解析为 String，如果成功，则将结果赋值给 ip。如果失败，跳过这一行。
      let res = ip
        .trim_end()
        .split(",")
        .last()
        .unwrap()
        .parse::<f32>() //将最后一个字段解析为 f32 类型，如果解析失败则返回 0.0
        .unwrap_or(0.0) as i64;

      sum += res;
    }
  }

  sum
}

// useful function to read the lines from a file
fn read_lines<P>(filename: P) -> io::Result<io::Lines<io::BufReader<File>>> where P: AsRef<Path>,{ // 泛型约束：P: AsRef<Path>，这意味着泛型参数 P 必须能够转换为 Path
  let file = File::open(filename)?;
  Ok(io::BufReader::new(file).lines())
}
